/*

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

package main

// import "fmt"
import "io"
import "net/http"
import "net/url"
import "os"
import "time"
import "github.com/golang/groupcache/lru"
import "strings"
import "github.com/kz26/m3u8"
import "path/filepath"


func doRequest(c *http.Client, req *http.Request) (*http.Response, error) {
	req.Header.Set("User-Agent", "gohls/xiechc")
	resp, err := c.Do(req)
	return resp, err
}

type Download struct {
	URI string
	totalDuration time.Duration
}

func downloadSegment(dlc chan *Download, recTime time.Duration, gid string) {
        os.Mkdir(gid, os.ModePerm)
        var client = &http.Client{}
	for v := range dlc {
	        segmenturl, err := url.Parse(v.URI)
	        if err != nil {
	            logger.Println(err)
	        }
                file := filepath.Base(segmenturl.Path)
                rfile := filepath.Join(gid, file)
	        out, err := os.Create(rfile)
	        if err != nil {
	            logger.Panic(err)
	        }
		req, err := http.NewRequest("GET", v.URI, nil)
		if err != nil {
	            logger.Println(err)
		}
		resp, err := doRequest(client, req)
		if err != nil {
		    continue
		}
		if resp.StatusCode != 200 {
		    continue
		}
		_, err = io.Copy(out, resp.Body)
		if err != nil {
	            logger.Println(err)
		}
		resp.Body.Close()
		logger.Printf("Downloaded %v\n", rfile)
	        out.Close()
                /*
		if recTime != 0 {
			log.Printf("Recorded %v of %v\n", v.totalDuration, recTime)
			} else {
				log.Printf("Recorded %v\n", v.totalDuration)
			}
                */
	}
}

func getPlaylist(urlStr string, recTime time.Duration, useLocalTime bool, dlc chan *Download) {
        var client = &http.Client{}
	startTime := time.Now()
	var recDuration time.Duration = 0
	cache := lru.New(1024)
	playlistUrl, err := url.Parse(urlStr)
	if err != nil {
		logger.Println(err)
	}
	for {
		req, err := http.NewRequest("GET", urlStr, nil)
		if err != nil {
		    logger.Println(err)
		}
		resp, err := doRequest(client, req)
		if err != nil {
		    logger.Println(err)
	            time.Sleep(time.Duration(3) * time.Second)
		}
		playlist, listType, err := m3u8.DecodeFrom(resp.Body, true)
		if err != nil {
		    logger.Println(err)
		}
		resp.Body.Close()
		if listType == m3u8.MEDIA {
			mpl := playlist.(*m3u8.MediaPlaylist)
			for _, v := range mpl.Segments {
				if v != nil {
					var msURI string
					if strings.HasPrefix(v.URI, "http") {
						msURI, err = url.QueryUnescape(v.URI)
						if err != nil {
							logger.Println(err)
						}
					} else {
						msUrl, err := playlistUrl.Parse(v.URI)
						if err != nil {
							continue
						}
						msURI, err = url.QueryUnescape(msUrl.String())
						if err != nil {
		                                    logger.Println(err)
						}
					}
					_, hit := cache.Get(msURI)
					if !hit {
						cache.Add(msURI, nil)
						if useLocalTime {
                                                    recDuration = time.Now().Sub(startTime)
						} else {
                                                    recDuration += time.Duration(int64(v.Duration * 1000000000))
						}
						dlc <- &Download{msURI, recDuration}
					}
					if recTime != 0 && recDuration != 0 && recDuration >= recTime {
                                            close(dlc)
                                            return
					}
				}
			}
			if mpl.Closed {
			    close(dlc)
			    return
			} else {
			    time.Sleep(time.Duration(int64(mpl.TargetDuration * 1000000000)))
			}
		} else {
			logger.Println("Not a valid media playlist")
		}
	}
}

func Downloader(gid string,  location string) {

	duration := time.Duration(0)
	useLocalTime := false

	msChan := make(chan *Download, 1024)
	go getPlaylist(location, duration, useLocalTime, msChan)
	downloadSegment(msChan, duration, gid)
}
/*
func main(){
   go Downloader("cctv", "http://ivi.bupt.edu.cn/hls/cctv1.m3u8")
   time.Sleep(time.Hour)
}
*/
